#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>

#include <linux/miscdevice.h>

#include <linux/syscalls.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/gpio.h>

#include <linux/regulator/consumer.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/max8649.h>
#include <linux/regulator/fixed.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <linux/notifier.h>
#include <linux/fb.h>
#include "siw_touch.h"

// Touch Event type define
#define KEY_PRESS 1
#define KEY_RELEASE 0
#define PRESS_MAX 255

#define TS_EVENT_UNKNOWN 0x00
#define TS_EVENT_PRESS 0x01
#define TS_EVENT_MOVE 0x02
#define TS_EVENT_RELEASE 0x03

#define TS_EVENT_BACK 0x05
#define TS_EVENT_MENU 0x06
#define TS_EVENT_DETACH 0x07

// DEVICE NAME
#define MODULE_NAME "SIW_TOUCH"
#define MISC_NAME "siw_touch"
#define SIW_DEV_NAME "SIW_TOUCH"
#define FW_CORE 0
// I2C Protocol
#define IIC_CODE_START '$' // 0x24
#define FW_PACKET_SIZE 60

#ifndef HAVE_UNLOCKED_IOCTL
#define HAVE_UNLOCKED_IOCTL 0
#endif

extern bool usb_is_connected;
extern bool dc_is_connected;

typedef enum
{
    IICCMD_RESET = 0xA0, // Recieve size = 0 | response size = 1
    IICCMD_FWUP,         // Don't Move It!!
    IICCMD_GET_SIZE,     // Recieve size = 0 | response size = 3 [ Reserved SizeX SizeY ]
    IICCMD_GET_FRAMECNT,
    IICCMD_SET_PARAM,    // Recieve size = [ Reserved Mode Param[0] Param[1] ] | response size = 0
    IICCMD_GET_DBG,      // Recieve size = 0 | response size = SizeX*SizeY*2
    IICCMD_GET_EVENTNUM, // Recieve size = 0 | response size = N
    IICCMD_GET_EVENT,    // Recieve size = 0 | response size = N
    IICCMD_GET_MSG,      // Recieve size = 0 | response size = N
    IICCMD_GET_SNR,      // 0xA9 : SNR
    IICCMD_SET_SLEEP,    // 0xAA : Deep Power Down
    IICCMD_SET_WAKE,     // 0xAB : Wakeup
    IICCMD_NOP,          // NO Operation

    IICCMD_GET_CHAGER_ON = 0xB0,
    IICCMD_GET_CHAGER_OFF = 0xB1,

    IICCMD_FW_RESET = 0xE1,
    IICCMD_FW_FLASHERASE,
    IICCMD_FW_INFO_DATA,
    IICCMD_FW_BOOTMODE,
} IICCMD;

#define SIZEOF_HEAD_TRANSMSG (4)
#define DEF_SECTOR_SIZE (512)

typedef struct
{
    unsigned short uMsgSize;
    unsigned short uMsgID;
    unsigned char ptrMsg[DEF_SECTOR_SIZE];
} stTRANSMSG;

typedef enum
{
    ID_TRANSMSG_FWVERSION = 0,
    ID_TRANSMSG_FRAMESIZE,
    ID_TRANSMSG_FAIL = 0xFFFF,
} enumTRANSMSGID;

typedef struct
{
    unsigned char id;
    unsigned char event;
    short x;
    short y;
    short strength;
} stTouchData;

struct ts_event
{
    u8 touch_point;
    u16 au16_x[CFG_MAX_TOUCH_POINTS];     // x coordinate
    u16 au16_y[CFG_MAX_TOUCH_POINTS];     // y coordinate
    u8 touch_event[CFG_MAX_TOUCH_POINTS]; // event type
    u8 finger_id[CFG_MAX_TOUCH_POINTS];   // touch ID
    u8 area[CFG_MAX_TOUCH_POINTS];        // finger area
    u16 pressure;
};

struct siw_ts_data
{
    struct i2c_client *client;
    struct input_dev *input_dev;
    struct ts_event event;
    struct work_struct pen_event_work;
    struct workqueue_struct *ts_workqueue;
    struct notifier_block notifier;
    struct mutex device_mode_mutex; /* Ensures that only one function can specify the Device Mode at a time. */
};
static struct i2c_client *this_client;
static struct siw_ts_data *g_siw_ts;

typedef struct _firmware_info
{
    unsigned char crc[64];
    unsigned char version[8];
    unsigned char date[14];
    unsigned char size[4];
} firmware_info;

firmware_info *fwinfo;

// * 하드웨어 키 값을 저장하는 배열
int tsp_keycodes[CFG_NUMOFKEYS] = {
    KEY_MENU,
    KEY_HOME,
    KEY_BACK,
    KEY_SEARCH};

int gtp_rst_gpio;
int gtp_int_gpio;

// 하드웨어 키 값이 Press or Release 되었는지를 확인하는 배열
static bool tsp_keystatus[CFG_NUMOFKEYS];
static int siw_hw_reset(void);
void siw_touch_on(void);
void siw_touch_off(void);

static int siw_i2c_rxdata(char *txdata, int tx_len, char *rxdata, int rx_len)
{
    struct i2c_msg xfer[2];

    xfer[0].addr = this_client->addr;
    xfer[0].flags = 0;
    xfer[0].len = tx_len;
    xfer[0].buf = txdata;
    /* xfer[0].scl_rate = 400 * 1000; */

    xfer[1].addr = this_client->addr;
    xfer[1].flags = I2C_M_RD;
    xfer[1].len = rx_len;
    xfer[1].buf = rxdata;
    /* xfer[1].scl_rate = 400 * 1000; */

    if (i2c_transfer(this_client->adapter, xfer, 2) != 2)
    {
        printk("i2c read error\n");
        return -1;
    }

    return rx_len;
}

static int siw_i2c_rxdata_only(char *rxdata, int rx_len)
{
    int ret;
    ret = i2c_smbus_read_i2c_block_data(this_client, this_client->addr, rx_len, (u8 *)rxdata);
    return ret;
}

static int siw_i2c_txdata(char *txdata, int length)
{
    if (i2c_master_send(this_client, txdata, length) != length)
    {
        printk("i2c write error\n");
        return -1;
    }
    return length;
}

int siw_touch_key_process(struct input_dev *dev, int x, int y, int touch_event)
{
    int keyID, i;

    switch (touch_event)
    {
    case TS_EVENT_MENU:
        keyID = KEY_MENU;
        break;
    case TS_EVENT_BACK:
        keyID = KEY_BACK;
        break;
    case TS_EVENT_DETACH:
        keyID = KEY_RESERVED;
        break;
    default:
        return 0;
    }

    for (i = 0; i < CFG_NUMOFKEYS; i++)
    {
        if (!keyID && tsp_keystatus[i])
        {
            input_report_key(dev, tsp_keycodes[i], 0);
            tsp_keystatus[i] = KEY_RELEASE;
        }
        else if ((keyID == tsp_keycodes[i]) && !tsp_keystatus[i])
        {
            input_report_key(dev, tsp_keycodes[i], 1);
            tsp_keystatus[i] = KEY_PRESS;
        }
    }

    return 1;
}

static int siw_read_frame_data(unsigned char *buf, int size)
{
    u8 cmd[2] = {IIC_CODE_START, IICCMD_GET_DBG};
    int ret;

    ret = siw_i2c_rxdata(cmd, sizeof(cmd), buf, size);

    if (ret < 0)
    {
        SIW_TOUCH_DBG("read failed: %d\n", ret);
        return ret;
    }

    return ret;
}

//read touch point information
static int siw_read_data(void)
{
    struct siw_ts_data *data = i2c_get_clientdata(this_client);
    struct ts_event *event = &data->event;
    u8 cmd[2] = {IIC_CODE_START, IICCMD_GET_EVENTNUM};
    u8 rcv[100] = {0};
    u8 nFingers = 0;
    stTouchData *ptrTouchData;
    int ret = -1;
    int cnt;
    int i;

    //	printk(KERN_EMERG "WYB: func = %s, line = %d\n",__func__,__LINE__);
    ret = siw_i2c_rxdata(cmd, sizeof(cmd), rcv, 2);
    if (ret < 0)
    {
        SIW_TOUCH_DBG("read failed: %d\n", ret);
        return ret;
    }

    nFingers = rcv[0];
    if (nFingers)
    {
        if (nFingers > CFG_MAX_TOUCH_POINTS)
        {
            printk("##ERR, nFinger:%d\n", nFingers);
            event->touch_point = 0;
            return ret;
        }
        cnt = sizeof(stTouchData) * nFingers + 2;
        cmd[1] = IICCMD_GET_EVENT;

        siw_i2c_rxdata(cmd, 2, rcv, cnt);
    }
    else
    {
        printk("##ERR-No fingers\n");
        event->touch_point = 0;
        return ret;
    }

    ptrTouchData = (stTouchData *)&rcv[2];
    memset(event, 0, sizeof(struct ts_event));
    event->touch_point = nFingers;

    for (cnt = 0; cnt < nFingers; cnt++)
    {
        if (ptrTouchData->id >= CFG_MAX_TOUCH_POINTS)
        {
            printk("##ERR(cnt:%d, nFingers:%d), ID:%d\n", cnt, nFingers, ptrTouchData->id);
            event->touch_point = 0;
            return -1;
        }

        event->au16_x[cnt] = ptrTouchData->x;
        event->au16_y[cnt] = ptrTouchData->y;
        event->touch_event[cnt] = ptrTouchData->event;
        event->finger_id[cnt] = ptrTouchData->id;
        event->area[cnt] = ptrTouchData->strength;

        ptrTouchData++;
    }

    event->pressure = 200;

    if (event->touch_point == 0)
    {
        return 0;
    }

    for (i = 0; i < event->touch_point; i++)
    {

        unsigned short x = event->au16_x[i];
        unsigned short y = event->au16_y[i];

#ifdef SIW_ROTATE_X
        x = SCREEN_MAX_X - event->au16_x[i];
#endif
#ifdef SIW_ROTATE_Y
        y = SCREEN_MAX_Y - event->au16_y[i];
#endif

        x = x * SCREEN_MAX_X / 32767;
        y = y * SCREEN_MAX_Y / 32767;

        //		printk(KERN_EMERG "WYB: x = %d, y = %d\n",x,y);
        switch (event->touch_event[i])
        {
        case TS_EVENT_MOVE:
        case TS_EVENT_PRESS:
            input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->finger_id[i]);
            input_report_abs(data->input_dev, ABS_MT_POSITION_X, x);
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y, y);
            input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->area[i]);
            input_report_abs(data->input_dev, ABS_MT_PRESSURE, 200);
            input_mt_sync(data->input_dev);
            break;
        case TS_EVENT_RELEASE:
            input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, event->finger_id[i]);
            //	input_report_abs(data->input_dev, ABS_MT_POSITION_X, x);
            //	input_report_abs(data->input_dev, ABS_MT_POSITION_Y, y);
            input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, 0); //ABS_MT_TOUCH_MAJOR=0ߍܡѻɏΪup
            input_report_abs(data->input_dev, ABS_MT_PRESSURE, 0);
            input_mt_sync(data->input_dev);
            break;
        case TS_EVENT_MENU:
        case TS_EVENT_BACK:
        case TS_EVENT_DETACH:
            break;
        }
    }
    input_sync(data->input_dev);
    return 0;
}

static irqreturn_t siw_ts_interrupt(int irq, void *dev_id)
{
    siw_read_data();
    return IRQ_HANDLED;
}

void siw_touch_on(void)
{
    int err = 0;
    err = siw_hw_reset();
    if (err)
    {
        SIW_TOUCH_DBG("siw power ERR\n");
    }
}

void siw_touch_off(void)
{
    mdelay(10);
}

static int siw_hw_reset(void)
{
    /* gpio_request(gtp_int_gpio,"TS_INT"); */

    gpio_set_value(gtp_rst_gpio, 0);
    mdelay(1);
    gpio_direction_output(gtp_int_gpio, 0);
    mdelay(100);

    gpio_set_value(gtp_rst_gpio, 1);
    mdelay(10);

    gpio_direction_input(gtp_int_gpio);
    SIW_TOUCH_DBG("siw_hw_reset done\n");

    mdelay(500);

    return 0;
}

/* static int siw_status_pin_control(int value) */
/* { */
/* 	return	0; */
/* } */

/* static void siw_fw_upgrade_reset_status_init(void) */
/* { */

/* } */

static int siw_get_firmware_version(char *fw_ver_ch_p)
{

    int i;
    char cmd[31] = {IIC_CODE_START, IICCMD_GET_MSG};
    char rdata[32];

    stTRANSMSG *ptrTxMsg = NULL;
    stTRANSMSG *ptrRxMsg = NULL;

    // Touch screen information display
    ptrTxMsg = (stTRANSMSG *)&cmd[2];
    ptrTxMsg->uMsgSize = SIZEOF_HEAD_TRANSMSG + 8; // RX/TX Size should be equal.
    ptrTxMsg->uMsgID = ID_TRANSMSG_FWVERSION;      // ID_GETMSG_FWVER Get FW Version

    ptrRxMsg = (stTRANSMSG *)&rdata[0];

    if (siw_i2c_rxdata(cmd, (ptrTxMsg->uMsgSize + 2), rdata, (ptrTxMsg->uMsgSize)) < 0)
    {
        printk("siw_get_firmware_version rxdata error!\n");
        return -1;
    }

    return 0;
    //del by acl , because we just test i2c trans, ont need the version
    /*    
	SIW_TOUCH_DBG("siw_get_firmware_version: %c%c%c%c%c%02d.%02d.%02d\n", 
                                            ptrRxMsg->ptrMsg[0], ptrRxMsg->ptrMsg[1],
                                            ptrRxMsg->ptrMsg[2], ptrRxMsg->ptrMsg[3],
                                            ptrRxMsg->ptrMsg[4],ptrRxMsg->ptrMsg[5],
                                            ptrRxMsg->ptrMsg[6],ptrRxMsg->ptrMsg[7]);
  

    for(i=0;i<8;i++)
    {
        fw_ver_ch_p[i] = ptrRxMsg->ptrMsg[i];
    }

    if ((fw_ver_ch_p[0] == 'T')
        && (fw_ver_ch_p[1] == 'H')
        && (fw_ver_ch_p[2] == 'K'))
       return 1;
    else
       return -1;
*/
}

//shlee TODO

static int siw_i2c_send_fw_xfer_cmd(unsigned char *buf, short buf_len)
{
    unsigned char cmd[4];
    unsigned char rSize[2];
    unsigned char fwdata[61];
    unsigned char checkSum = 0;
    int i;

    SIW_TOUCH_DBG("siw_i2c_send_fw_xfer_cmd \n");

    cmd[0] = IIC_CODE_START;
    cmd[1] = IICCMD_FWUP;
    cmd[2] = (buf_len & 0xff);
    cmd[3] = (buf_len >> 8);

    // Fw Packet Size Write
    if (siw_i2c_rxdata(cmd, 4, rSize, 2) < 0)
    {
        printk("siw_i2c_send_fw_xfer_cmd error!\n");
        return -1;
    }

    // FW Packet Size read
    if ((cmd[2] != rSize[0]) || (cmd[3] != rSize[1]))
    {
        printk("siw_i2c_send_fw_xfer_cmd read size error!\n");
        return -1;
    }

    // Write FW
    memset(fwdata, 0xff, sizeof(fwdata));
    for (i = 0; i < buf_len; i++)
    {
        fwdata[i] = buf[i];
        checkSum += fwdata[i];
    }
    fwdata[buf_len] = checkSum;

    if (siw_i2c_txdata(fwdata, (buf_len + 1)) < 0)
    {
        printk("siw_i2c_send_fw_xfer_cmd fw write error!\n");
        return -1;
    }

    mdelay(10);

    if (siw_i2c_rxdata_only(rSize, 1) < 0)
    {
        printk("siw_i2c_send_fw_xfer_cmd fw read error!\n");
        return -1;
    }

    if (checkSum != rSize[0])
    {
        printk("siw_i2c_send_fw_xfer_cmd read checksum error!\n");
        return -1;
    }

    return 0;
}

static int siw_i2c_send_fw_info_cmd(unsigned char *crc, unsigned char *version, unsigned char *date, unsigned char *size)
{
    unsigned char txbuf[93];
    unsigned char checkSum = 0;
    unsigned char rCheckSum = 0;
    int i;

    SIW_TOUCH_DBG("siw_i2c_send_fw_info_cmd\n");

    txbuf[0] = IIC_CODE_START;
    txbuf[1] = IICCMD_FW_INFO_DATA;

    SIW_TOUCH_DBG("CRC :");
    for (i = 0; i < 64; i++)
    {
        txbuf[i + 2] = crc[i];
        checkSum += crc[i];
        SIW_TOUCH_DBG("0x%02x ", crc[i]);
    }
    SIW_TOUCH_DBG("\n");

    SIW_TOUCH_DBG("VERSION :");
    for (i = 0; i < 8; i++)
    {
        txbuf[i + 66] = version[i];
        checkSum += version[i];
        SIW_TOUCH_DBG("0x%02x ", version[i]);
    }
    SIW_TOUCH_DBG("\n");

    SIW_TOUCH_DBG("DATE :");
    for (i = 0; i < 14; i++)
    {
        txbuf[i + 74] = date[i];
        checkSum += date[i];
        SIW_TOUCH_DBG("0x%02x ", date[i]);
    }
    SIW_TOUCH_DBG("\n");

    SIW_TOUCH_DBG("SIZE:");
    for (i = 0; i < 4; i++)
    {
        txbuf[i + 88] = size[i];
        checkSum += size[i];
        SIW_TOUCH_DBG("0x%02x ", size[i]);
    }
    SIW_TOUCH_DBG("\n");
    txbuf[92] = checkSum;

    if (siw_i2c_txdata(txbuf, 93) < 0)
    {
        printk("siw_i2c_send_fw_info_cmd (tx) error!\n");
        return -1;
    }

    mdelay(200);

    if (siw_i2c_rxdata_only(&rCheckSum, 1) < 0)
    {
        printk("siw_i2c_send_fw_info_cmd (rx) error!\n");
        return -1;
    }

    if (checkSum != rCheckSum)
    {
        printk("siw_i2c_send_fw_info_cmd CheckSum (0x%02x, 0x%02x) failed!\n", checkSum, rCheckSum);
        return -1;
    }

    return 0;
}

static int siw_i2c_send_reset_cmd(void)
{
    char cmd[2] = {IIC_CODE_START, IICCMD_RESET};
    int ret;

    SIW_TOUCH_DBG("siw_i2c_send_reset_cmd\n");

    ret = siw_i2c_txdata(cmd, sizeof(cmd));
    mdelay(500);
    if (ret < 0)
    {
        SIW_TOUCH_ERR("siw_sw_reset error!!\n");
        return -1;
    }

    return 0;
}

static int siw_i2c_send_fw_reset_cmd(void)
{
    unsigned char cmd[2];
    int ret;

    SIW_TOUCH_DBG("siw_i2c_send_fw_reset_cmd\n");

    cmd[0] = IIC_CODE_START;
    cmd[1] = IICCMD_FW_RESET;

    ret = siw_i2c_txdata(cmd, sizeof(cmd));
    mdelay(300);

    if (ret < 0)
    {
        printk("siw_i2c_send_fw_reset_cmd error!\n");
        return -1;
    }

    return 0;
}

static int siw_i2c_send_erase_cmd(void)
{
    unsigned char cmd[2];
    unsigned char rcv[2];

    SIW_TOUCH_DBG("siw_i2c_send_erase_cmd\n");

    cmd[0] = IIC_CODE_START;
    cmd[1] = IICCMD_FW_FLASHERASE;

    if (siw_i2c_txdata(cmd, 2) < 0)
    {
        printk("siw_i2c_send_erase_cmd error!\n");
        return -1;
    }

    mdelay(400);

    // read echo cmd 1 packet
    if (siw_i2c_rxdata_only(rcv, 1) < 0)
    {
        printk("siw_i2c_send_erase_cmd(rx) error!\n");
        return -1;
    }

    //printk("siw_i2c_send_erase_cmd echo 0x%02x %d\n", rcv[0], rcv[0]);

    //if(siw_i2c_txdata(cmd, sizeof(cmd)) < 0)
    //{
    //   printk("siw_i2c_send_erase_cmd error!\n");
    //    return -1;
    //}

    return 0;
}

static int siw_i2c_bootmode_check(void)
{
    unsigned char cmd[2];
    unsigned char rcv[2];

    cmd[0] = IIC_CODE_START;
    cmd[1] = IICCMD_FW_BOOTMODE;

    if (siw_i2c_rxdata(cmd, 2, rcv, 2) < 0)
    {
        printk("siw_i2c_bootmode_check error!\n");
        return -1;
    }

    //printk("(%s)-(%d) rcv[0] : 0x%02x, rcv[1] : 0x%02x\n",__FUNCTION__,__LINE__, rcv[0], rcv[1]);

    // return 0 : bootloader state
    // return 1 : main firmware state
    if ((rcv[0] == 'R') && (rcv[1] == '0'))
        return 0;
    else if ((rcv[0] == 'R') && (rcv[1] == '1'))
        return 1;
    return 1;
}

#define FW_CRC_SIZE 64
#define FW_VERSION_SIZE 8
#define FW_DATE_SIZE 14
#define FW_FILE_SIZE 4
#if 0
static int siw_read_firmware_info(void)
{

#if FW_CORE
    for(i=0;i<FW_CRC_SIZE;i++)
        fwinfo->crc[i] = FIRMWARE_CODE[i];

    for(i=0;i<FW_VERSION_SIZE;i++)
        fwinfo->version[i] = FIRMWARE_CODE[FW_CRC_SIZE + i];

    for(i=0;i<FW_DATE_SIZE;i++)
        fwinfo->date[i] = FIRMWARE_CODE[FW_CRC_SIZE + FW_VERSION_SIZE + i];

    for(i=0;i<FW_FILE_SIZE;i++)
        fwinfo->size[i] = FIRMWARE_CODE[FW_CRC_SIZE + FW_VERSION_SIZE + FW_DATE_SIZE + 4 /* padding */+ i];
#endif
    /*
    printk("HDR CRC: ");
    for(i=0;i<FW_CRC_SIZE;i++)
        printk("0x%02x ", fwinfo->crc[i]);
    printk("\n");

    printk("HDR VERSION : %c%c%c%c%c %d.%d.%d\n", fwinfo->version[0], fwinfo->version[1], 
                                            fwinfo->version[2],  fwinfo->version[3], 
                                            fwinfo->version[4],  fwinfo->version[5], 
                                            fwinfo->version[6],  fwinfo->version[7]); 

    printk("HDR DATE : %c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c \n", fwinfo->date[0], fwinfo->date[1], 
                                            fwinfo->date[2],  fwinfo->date[3], fwinfo->date[4],
                                            fwinfo->date[5], fwinfo->date[6], fwinfo->date[7],
                                            fwinfo->date[8], fwinfo->date[9], fwinfo->date[10],
                                            fwinfo->date[11], fwinfo->date[12], fwinfo->date[13],
                                            fwinfo->date[14]);

    printk("HDR SIZE : 0x%x 0x%x 0x%x 0x%x\n", fwinfo->size[0], fwinfo->size[1], fwinfo->size[2], fwinfo->size[3]); 
    */
    return 0;
    
}

static void siw_firmware_update(void)
{
    int fw_length;
    int fw_write_cnt;
    int fw_data_loc;
    int fw_write_size;
    unsigned char hw_version[8];

    int retry = 0;
    int ret;
    int i;

    for (i=0;i<5;i++)
    {    
        ret = siw_i2c_bootmode_check();
        if (ret == 0) // bootloader mode
        {
            break;
        }
        else
        {
            siw_i2c_send_fw_reset_cmd();
            retry++;
        }
    }

    if ((retry == 5) && (ret != 0))
    {
        printk("siw_firmware_update fail to bootloader mode!\n");
        return;
    }

    // Send Erase
    if (siw_i2c_send_erase_cmd() < 0)
    {
        printk("siw_i2c_send_erase_cmd failed ! \n");
        return;
    }

    // Send Info Data
    if (siw_i2c_send_fw_info_cmd(fwinfo->crc, fwinfo->version, fwinfo->date, fwinfo->size) < 0)
    {
        printk("siw_i2c_send_fw_info_cmd failed ! \n");
        return;
    }

    // Send Firmware Data
#define FW_PACKET_SIZE 60
    fw_length = ((fwinfo->size[0] << 24) | (fwinfo->size[1] << 16) | (fwinfo->size[2] << 8) | (fwinfo->size[3] << 0));
    fw_write_cnt = fw_length / FW_PACKET_SIZE;
    if ((fw_length % FW_PACKET_SIZE) > 0) fw_write_cnt++;

#define FW_BOOTLOADER_OFFSET 0x400 //(1KB Info Sector Offset)
    fw_data_loc = 0; 

    SIW_TOUCH_DBG("fw_length : %d, fw_write_cnt :%d, fw_data_loc:%d\n", fw_length, fw_write_cnt, fw_data_loc);

    for(i=0;i<fw_write_cnt;i++)
    {
        if ((fw_length - fw_data_loc) < FW_PACKET_SIZE)
            fw_write_size = fw_length - fw_data_loc;
        else
            fw_write_size = FW_PACKET_SIZE;
#if FW_CORE
        if (siw_i2c_send_fw_xfer_cmd(&FIRMWARE_CODE[FW_BOOTLOADER_OFFSET + fw_data_loc], fw_write_size) < 0)
        {
            printk(" siw_i2c_send_fw_xfer_cmd failed!\n");
            return -1;
        }
#endif
        fw_data_loc += fw_write_size; 
        SIW_TOUCH_DBG("fw_length : 0x%x,  fw_data_loc : 0x%x\n", fw_length, fw_data_loc);
        printk(".");
    }
    printk("\n");

    // Reset
    if (siw_i2c_send_reset_cmd() < 0)
    {
        printk("siw_i2c_reset_cmd failed ! \n");
        return;
    }

    // Check Mode
    ret = siw_i2c_bootmode_check();
    if (ret != 1) // main firmware mode
    {
        printk("siw_i2c_bootmode_check failed!\n");
        return;
    }

    // Check Version
    if (siw_get_firmware_version(hw_version)<0)
    {
        printk("error! siw_get_firmware_version\n");
        return;
    }

    printk("[SIW_TOUCH] UPDATED VERSION : %c%c%c%c%c %02d.%02d.%02d\n", hw_version[0], hw_version[1], 
                                            hw_version[2], hw_version[3], 
                                            hw_version[4], hw_version[5], 
                                            hw_version[6], hw_version[7]); 
    printk("[SIW_TOUCH] update complete!\n");

    return;
}

static int siw_update_check(void)
{
    unsigned char hw_version[8];
    int fw_info_version = 0, hw_version_num = 0;
    int update_state = 0;
    int ret;

    // read fw_header binary
    siw_read_firmware_info();

    ret = siw_i2c_bootmode_check();
    if (ret != 1)
    {
        printk("NOW BOOT MODE --> FIRMWARE UPDATE\n");
        update_state = 1;
    }

    // read hw version
    if (update_state == 0)
    {
        if (siw_get_firmware_version(hw_version)<0)
        {
            printk("error! siw_get_firmware_version. Go to update mode.\n");
        } //main souce에서 버젼정보를 못보내준다면 새로 업데이트되도록 수정

        // compare version
        fw_info_version = fwinfo->version[5] << 16;
        fw_info_version |= fwinfo->version[6] << 8;
        fw_info_version |= fwinfo->version[7]; 

	hw_version_num = hw_version[5]<<16;
	hw_version_num |= hw_version[6]<<8;
	hw_version_num |= hw_version[7];

	if (fw_info_version > hw_version_num)
	{
		update_state = 1;
	}
    }

    if (update_state == 0)
    {
        printk("[SIW_TOUCH] firmware version is lastest.\n");
        printk("[SIW_TOUCH] VERSION : %c%c%c%c%c %02d.%02d.%02d\n", hw_version[0], hw_version[1], 
                                            hw_version[2], hw_version[3], 
                                            hw_version[4], hw_version[5], 
                                            hw_version[6], hw_version[7]); 
    }
    else if (update_state == 1)
    {
        printk("[SIW_TOUCH] firmware version is old. Go to update mode.\n");
        printk("[SIW_TOUCH] HW_VERSION [%c%c%c%c%c %02d.%02d.%02d] ", hw_version[0], hw_version[1], 
                                            hw_version[2], hw_version[3], 
                                            hw_version[4], hw_version[5], 
                                            hw_version[6], hw_version[7]); 

        printk("<==> UPDATE_VERSION [%c%c%c%c%c %02d.%02d.%02d] \n", 
                                            fwinfo->version[0], fwinfo->version[1], 
                                            fwinfo->version[2], fwinfo->version[3], 
                                            fwinfo->version[4], fwinfo->version[5], 
                                            fwinfo->version[6], fwinfo->version[7]); 


        siw_firmware_update();
    }
    return 0;
}
#endif
// IOCTL
static int open(struct inode *inode, struct file *file)
{
    return 0;
}

static int release(struct inode *inode, struct file *file)
{
    return 0;
}

static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
    return 0;
}

static ssize_t write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
    return 0;
}
#define IOCTL_GET_FRAME 0x9121

#define IOCTL_GET_FRAME 0x9121
#define IOCTL_GET_VERSION 0x9201
#define IOCTL_SET_FW_RESET 0x9202
#define IOCTL_SET_RESET 0x9203
#define IOCTL_SET_FW_ERASE 0x9204
#define IOCTL_GET_BOOTMODE 0x9205
#define IOCTL_SET_FW_INFO 0x9206
#define IOCTL_SET_FW_DATA 0x9207

static u8 siw_send_buf[COLUMN_CELL_SIZE * ROW_CELL_SIZE * 2];
#if HAVE_UNLOCKED_IOCTL
static long unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#else
static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
#endif
{
    u8 buf[COLUMN_CELL_SIZE * ROW_CELL_SIZE * 2];
    int ret;
    int i;
    int cnt;
    unsigned char hw_version[8];
    unsigned char fwbuf[61];
    unsigned char fwinfodata[91];

    memset(buf, 0x0, sizeof(buf));
    memset(siw_send_buf, 0x0, sizeof(siw_send_buf));

    switch (cmd)
    {
    case IOCTL_GET_FRAME:
        SIW_TOUCH_DBG("IOCTL_GET_FRAME\n");
        ret = siw_read_frame_data(buf, COLUMN_CELL_SIZE * ROW_CELL_SIZE * 2);
        if (ret > 0)
        {
            cnt = 0;
            for (i = 0; i < COLUMN_CELL_SIZE * ROW_CELL_SIZE * 2; i++)
            {
                siw_send_buf[i] = buf[cnt++];
            }

            if (copy_to_user((void __user *)arg, siw_send_buf, sizeof(siw_send_buf)))
                return -EFAULT;
        }
        break;

    case IOCTL_GET_VERSION:
        SIW_TOUCH_DBG("IOCTL_GET_VERISON\n");
        if (siw_get_firmware_version(hw_version) < 0)
        {
            printk("IOCTL_GET_VERSION failed!\n");
            return -EFAULT;
        }

        if (copy_to_user((void __user *)arg, hw_version, sizeof(hw_version)))
            return -EFAULT;
        break;

    case IOCTL_SET_RESET:
        ret = siw_i2c_send_reset_cmd();
        SIW_TOUCH_DBG("IOCTL_SET_RESET : ret = %d\n", ret);
        if (ret < 0)
        {
            printk("IOCTL_SET_RESET failed!\n");
            return -EFAULT;
        }
        break;

    case IOCTL_SET_FW_RESET:
        ret = siw_i2c_send_fw_reset_cmd();
        SIW_TOUCH_DBG("IOCTL_SET_FW_RESET : ret = %d\n", ret);
        if (ret < 0)
        {
            printk("IOCTL_SET_FW_RESET failed!\n");
            return -EFAULT;
        }
        break;

    case IOCTL_SET_FW_ERASE:
        ret = siw_i2c_send_erase_cmd();
        SIW_TOUCH_DBG("IOCTL_SET_FW_ERASE : ret = %d\n", ret);
        if (ret < 0)
        {
            printk("IOCTL_SET_FW_ERASE failed!\n");
            return -1;
        }
        break;

    case IOCTL_GET_BOOTMODE:
        ret = siw_i2c_bootmode_check();
        SIW_TOUCH_DBG("IOCTL_GET_BOOTMODE : ret = %d\n", ret);
        if (copy_to_user((void __user *)arg, &ret, sizeof(int)))
            return -EFAULT;
        break;

    case IOCTL_SET_FW_INFO:
        SIW_TOUCH_DBG("IOCTL_SET_FW_INFO\n");
        ret = copy_from_user(fwinfodata, (void __user *)arg, 90);

        if (siw_i2c_send_fw_info_cmd(&fwinfodata[0], &fwinfodata[64], &fwinfodata[72], &fwinfodata[86]) < 0)
            return -EFAULT;
        break;

    case IOCTL_SET_FW_DATA:
        ret = copy_from_user(fwbuf, (void __user *)arg, FW_PACKET_SIZE);
        ret = siw_i2c_send_fw_xfer_cmd(fwbuf, FW_PACKET_SIZE);
        SIW_TOUCH_DBG("IOCTL_SET_FW_DATA : ret = %d\n", ret);
        if (ret < 0)
            return -EFAULT;
        break;
    }
    return 0;
}

static struct file_operations fops =
    {
        .owner = THIS_MODULE,
        .read = read,
        .write = write,
#if HAVE_UNLOCKED_IOCTL
        .unlocked_ioctl = unlocked_ioctl,
#else
        .ioctl = ioctl,
#endif
        .open = open,
        .release = release,
        .llseek = default_llseek /* using default implementation as declared in linux/fs.h */
};

static struct miscdevice miscdev =
    {
        .minor = MISC_DYNAMIC_MINOR,
        .name = MISC_NAME,
        .fops = &fops};

static void siw_ts_resume(struct siw_ts_data *ts)
{
    int err = 0;

    err = siw_hw_reset();
    if (err)
    {
        // TODO
    }

    enable_irq(this_client->irq);
}

static void siw_ts_suspend(struct siw_ts_data *ts)
{

    SIW_TOUCH_DBG("%s +++\n", __FUNCTION__);

    disable_irq_nosync(this_client->irq);
}
static int gtp_fb_notifier_callback(struct notifier_block *noti, unsigned long event, void *data)
{
    struct fb_event *ev_data = data;
    struct siw_ts_data *ts = container_of(noti, struct siw_ts_data, notifier);
    int *blank;
    static int is_enable = 0;
    if (ev_data && ev_data->data && event == FB_EVENT_BLANK && ts)
    {
        blank = ev_data->data;
        if (*blank == FB_BLANK_UNBLANK)
        {
            if (is_enable)
            {
                siw_ts_resume(ts);
                is_enable = 0;
            }
        }
        else if (*blank == FB_BLANK_POWERDOWN)
        {
            if (0 == is_enable)
            {
                siw_ts_suspend(ts);
                is_enable = 1;
            }
        }
    }

    return 0;
}

static void gtp_parse_dt(struct device *dev)
{
    struct device_node *np = dev->of_node;
    gtp_int_gpio = of_get_named_gpio(np, "touch-gpio", 0);
    gtp_rst_gpio = of_get_named_gpio(np, "reset-gpio", 0);
}

static int
siw_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    char fw_ver[32];
    struct siw_ts_data *siw_ts;
    struct input_dev *input_dev;
    int err = 0;
    int i;

    printk("________%s__\n", __func__);

    if (client->dev.of_node)
    {
        gtp_parse_dt(&client->dev);
    }

    gpio_request(gtp_rst_gpio, "TS_RST");

    if (err < 0)
    {
        printk("gpio_request(gtp_rst_gpio) ERROR!!!!!");
        return err;
    }

    siw_touch_on();

    mdelay(10);
    gpio_direction_output(gtp_rst_gpio, 1);
    mdelay(100);

    SIW_TOUCH_DBG("driver version is %s\n", SIW_DRIVER_VERSION);
    memset(fw_ver, 0, sizeof(fw_ver));

    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
    {
        dev_err(&client->dev, "%s: i2c function not supported\n", __func__);
        err = -ENODEV;
        goto err_check_functionality_failed;
    }

    siw_ts = kzalloc(sizeof(struct siw_ts_data), GFP_KERNEL);
    if (!siw_ts)
    {
        dev_err(&client->dev, "%s: Can't alloc mem\n", __func__);
        err = -ENOMEM;
        goto err_alloc_data_failed;
    }
    this_client = siw_ts->client = client;
    g_siw_ts = siw_ts;

    i2c_set_clientdata(client, siw_ts);

    err = siw_hw_reset();

    if (err)
    {
        dev_err(&client->dev, "%s: Can't reset Touch IC\n", __func__);
        goto err_create_singlethread;
    }

    err = siw_get_firmware_version(fw_ver);
    if (err)
    {
        SIW_TOUCH_DBG("Read FW Version ERR!(V%s)\n", fw_ver);
        goto err_create_singlethread;
    }
    else
    {
        SIW_TOUCH_DBG("Read FW Version OK!(V%s)\n", fw_ver);
    }

    fwinfo = kmalloc(sizeof(firmware_info), GFP_KERNEL);

    mutex_init(&siw_ts->device_mode_mutex);
    //siw_ts->client->irq = client->irq;

    gpio_direction_input(gtp_int_gpio);
    siw_ts->client->irq = gpio_to_irq(gtp_int_gpio);
    printk(KERN_EMERG "WYB: line = %d, int = %d\n", __LINE__, gtp_int_gpio);
    if (client->irq)
    {
        err = request_threaded_irq(client->irq, NULL, siw_ts_interrupt,
                                   IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->dev.driver->name, siw_ts);
        if (err < 0)
        {
            dev_err(&client->dev, "%s: request irq failed\n", __func__);
            goto err_irq_request_failed;
        }
    }
    disable_irq(client->irq);

    input_dev = input_allocate_device();
    if (!input_dev)
    {
        err = -ENOMEM;
        dev_err(&client->dev, "%s: failed to allocate input device\n", __func__);
        goto err_input_dev_alloc_failed;
    }

    siw_ts->input_dev = input_dev;

    //input_dev->name = SIW_DEV_NAME;
    input_dev->name = MODULE_NAME;

    siw_ts->input_dev->id.bustype = BUS_I2C;
    set_bit(EV_SYN, input_dev->evbit);
    set_bit(EV_KEY, input_dev->evbit);
    set_bit(EV_ABS, input_dev->evbit);

    set_bit(ABS_PRESSURE, input_dev->absbit);
    set_bit(INPUT_PROP_DIRECT, input_dev->propbit);

    input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);

    input_dev->keycode = tsp_keycodes;
    for (i = 0; i < CFG_NUMOFKEYS; i++)
    {
        input_set_capability(input_dev, EV_KEY, ((int *)input_dev->keycode)[i]);
        tsp_keystatus[i] = KEY_RELEASE;
    }

    err = input_register_device(input_dev);
    if (err)
    {
        dev_err(&client->dev, "%s: failed to register input device: %s\n",
                __func__, dev_name(&client->dev));
        goto err_input_register_device_failed;
    }

    siw_ts->notifier.notifier_call = gtp_fb_notifier_callback;
    fb_register_client(&siw_ts->notifier);

    err = misc_register(&miscdev);
    if (err)
    {
        dev_err(&client->dev, "%s: failed to register misc device: %s\n",
                __func__, dev_name(&client->dev));
    }

#if FW_CORE
    siw_update_check();
#endif
    /*
	err = siw_get_firmware_version(fw_ver);
	if(err) {
		SIW_TOUCH_DBG("Read FW Version ERR!(V%s)\n", fw_ver);
	} else {
		SIW_TOUCH_DBG("Read FW Version OK!(V%s)\n", fw_ver);
	}
*/
    enable_irq(client->irq);

    return 0;

err_input_register_device_failed:
    input_free_device(input_dev);
err_input_dev_alloc_failed:
    free_irq(client->irq, siw_ts);
err_irq_request_failed:
    cancel_work_sync(&siw_ts->pen_event_work);
    destroy_workqueue(siw_ts->ts_workqueue);
err_create_singlethread:
    i2c_set_clientdata(client, NULL);
    kfree(siw_ts);
err_alloc_data_failed:
err_check_functionality_failed:
    gpio_free(gtp_rst_gpio);
    return err;
}

/***********************************************************************************************
Name	:	 
Input	:	
Output	:	
function	:	
 ***********************************************************************************************/
static int siw_ts_remove(struct i2c_client *client)
{
    struct siw_ts_data *siw_ts;

    siw_ts = i2c_get_clientdata(client);
    mutex_destroy(&siw_ts->device_mode_mutex);
    free_irq(this_client->irq, siw_ts);
    input_unregister_device(siw_ts->input_dev);
    kfree(siw_ts);
    cancel_work_sync(&siw_ts->pen_event_work);
    destroy_workqueue(siw_ts->ts_workqueue);
    i2c_set_clientdata(client, NULL);
    return 0;
}

static const struct of_device_id siw_match_table[] = {
    {
        .compatible = "SIW_TOUCH",
    },
    {},
};

static const struct i2c_device_id siw_ts_id[] = {
    {MODULE_NAME, 0}, {}};

MODULE_DEVICE_TABLE(i2c, siw_ts_id);

static struct i2c_driver siw_ts_driver = {
    .probe = siw_ts_probe,
    .remove = siw_ts_remove,
    .id_table = siw_ts_id,
    .driver = {
        .name = MODULE_NAME,
        .owner = THIS_MODULE,
        .of_match_table = siw_match_table,
    },
};

/***********************************************************************************************
Name	:	 
Input	:	
Output	:	
function	:	
 ***********************************************************************************************/
static int __init siw_ts_init(void)
{
    int ret;
    printk("________%s___\n", __func__);
    ret = i2c_add_driver(&siw_ts_driver);

    return ret;
}

/***********************************************************************************************
Name	:	 
Input	:	
Output	:	
function	:	
 ***********************************************************************************************/
static void __exit siw_ts_exit(void)
{
    i2c_del_driver(&siw_ts_driver);
}

module_init(siw_ts_init);
module_exit(siw_ts_exit);

MODULE_AUTHOR("Silicon Works");
MODULE_DESCRIPTION("Silicon Works siw TouchScreen driver");
MODULE_LICENSE("GPL");
